var obj = {
    p1: [
        { p10: 10 },
        { p11: 11 },
        { p12: 12 },
        234,
        'sss',
        false,
        { 
            p13: [
                { p130: 130 },
                { p131: 131 },
            ]
        }
    ],
    p2: true,
    p3: {
        p31: 31,
        p32: 32,
    }
}
var arr = [obj]

// 1. 判断是数组就遍历数组，如果是普通的（非对象或数组）就直接赋值，
// 2. 判断是对象就遍历对象，判断每一个属性，如果是普工值，就直接赋值，
// 3. 不是普通值就调用自己
// 4. 最终返回一个
function deepClone1 (objOrArr) {
    if (typeof objOrArr !== 'object') {
        return objOrArr;
    }
    let o = objOrArr.length ? [] : {};
    for (key in objOrArr) {
        if (typeof objOrArr === 'object') {
            o[key] = deepClone(objOrArr[key]);
        } else {
            o[key] = objOrArr[key];
        }
    }
    return o;
}

// 上面的看似对，但是有问题
// 1. 判断是否为数组或对象的方法不够严谨
// 2. for in 容易把对象原型上的方法也拷贝到新对象中

function deepClone2 (oa) {
    if (typeof oa !== 'object') {
        return oa;
    }
    let str = Object.prototype.toString.call(oa);
    if (str.includes('Object') || str.includes('Array')) {
        // 对象处理、数组处理
        let obj = {};
        for (let key in oa) {
            if (oa.hasOwnProperty(key)) {
                obj[key] = deepClone2(oa[key]);
            }
        }
        return obj;
    }
}

console.log(deepClone2(obj))


// 上面的看似对，但是还有问题
// 1. 关于对象类型的数字，对象类型的字符串，其也是object的子类
// eg：var str = new String('abc')
// Object.prototype.toString.call(str)   // "[object String]"
// str === 'abc'  // false
// str.valueOf() === 'abc'  // true
// 所以修改如下

function deepClone3 (oa) {
    let str = Object.prototype.toString.call(oa);
    if (str.includes('Object') || str.includes('Array')) {
        // 对象处理、数组处理
        let obj = {};
        for (let key in oa) {
            if (oa.hasOwnProperty(key)) {
                obj[key] = deepClone3(oa[key]);
            }
        }
        return obj;
    } else {
        return oa.valueOf();
    }
}